package com.audaque.springboot.foshanupload.web.esdatademo.controller;


import com.audaque.springboot.foshanupload.web.esdatademo.model.pojo.Blog;
import com.audaque.springboot.foshanupload.web.esdatademo.repository.BlogRepository;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchPage;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Slf4j
@Api(tags = "增删改查（文档）")
@RestController
@RequestMapping("repository")
public class RepositoryController {
    @Autowired
    private BlogRepository blogRepository;
    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @ApiOperation("自定义评分查询")
    @PostMapping("functionScore")
    public Page<Blog> functionScore(String keyword, Integer pageNum, Integer pageSize, Integer sort) {
        Pageable pageable = PageRequest.of(pageNum, pageSize);
        NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
        //分页
        nativeSearchQueryBuilder.withPageable(pageable);

        //搜索
        if (StringUtils.isEmpty(keyword)) {
            nativeSearchQueryBuilder.withQuery(QueryBuilders.matchAllQuery());
        } else {
            List<FunctionScoreQueryBuilder.FilterFunctionBuilder> filterFunctionBuilders = new ArrayList<>();
            filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("title", keyword),
                    ScoreFunctionBuilders.weightFactorFunction(10)));
            filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("description", keyword),
                    ScoreFunctionBuilders.weightFactorFunction(5)));
            filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("content", keyword),
                    ScoreFunctionBuilders.weightFactorFunction(2)));
            FunctionScoreQueryBuilder.FilterFunctionBuilder[] builders = new FunctionScoreQueryBuilder.FilterFunctionBuilder[filterFunctionBuilders.size()];
            filterFunctionBuilders.toArray(builders);
            FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(builders)
                    .scoreMode(FunctionScoreQuery.ScoreMode.SUM)
                    .setMinScore(2);
            nativeSearchQueryBuilder.withQuery(functionScoreQueryBuilder);
        }
        //按相关度
        nativeSearchQueryBuilder.withSort(SortBuilders.scoreSort().order(SortOrder.DESC));

        NativeSearchQuery searchQuery = nativeSearchQueryBuilder.build();

        // 打印query语句
        log.debug(searchQuery.getQuery().toString());
        // 打印分页语句
        log.debug(searchQuery.getPageable().toString());
        // 打印排序语句
        log.debug(searchQuery.getElasticsearchSorts().toString());


        return blogRepository.search(searchQuery);
    }


    @ApiOperation("相关查询")
    @PostMapping("recommend")
    public Page<Blog> recommend(Long id, Integer pageNum, Integer pageSize) {
        Pageable pageable = PageRequest.of(pageNum, pageSize);

        Blog esBlog = blogRepository.findById(id).get();
        String keyword = esBlog.getTitle();

        List<FunctionScoreQueryBuilder.FilterFunctionBuilder> filterFunctionBuilders = new ArrayList<>();
        filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("title", keyword),
                ScoreFunctionBuilders.weightFactorFunction(8)));
        filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("content", keyword),
                ScoreFunctionBuilders.weightFactorFunction(2)));
        filterFunctionBuilders.add(new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.matchQuery("description", keyword),
                ScoreFunctionBuilders.weightFactorFunction(2)));
        FunctionScoreQueryBuilder.FilterFunctionBuilder[] builders = new FunctionScoreQueryBuilder.FilterFunctionBuilder[filterFunctionBuilders.size()];
        filterFunctionBuilders.toArray(builders);
        FunctionScoreQueryBuilder functionScoreQueryBuilder = QueryBuilders.functionScoreQuery(builders)
                .scoreMode(FunctionScoreQuery.ScoreMode.SUM)
                .setMinScore(2);
        //用于过滤掉相同的商品
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.mustNot(QueryBuilders.termQuery("id", id));
        //构建查询条件
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        builder.withQuery(functionScoreQueryBuilder);
        builder.withFilter(boolQueryBuilder);
        builder.withPageable(pageable);
        NativeSearchQuery searchQuery = builder.build();
        log.debug("DSL:{}", searchQuery.getQuery().toString());
        return blogRepository.search(searchQuery);

    }


    /**
     * 搜索博客
     *
     * @param key  搜索内容
     * @param page 页码
     * @param size 页大小
     * @return 搜索到的结果列表
     */
    @ApiOperation("高亮查询")
    @PostMapping("searchHighlight")
    public List<Blog> searchHighlight(String key, int page, int size) {
        // 1. 获取数据
        SearchPage<Blog> searchPage = blogRepository.findByDescriptiveContent(
                // 1.1 设置key和分页，这里是从第0页开始的，所以要-1
                key, PageRequest.of(page - 1, size));
        // 2. 高亮数据替换
        List<SearchHit<Blog>> searchHitList = searchPage.getContent();
        ArrayList<Blog> blogDocList = new ArrayList<>(searchHitList.size());
        for (SearchHit<Blog> blogHit : searchHitList) {
            // 2.1 获取博客数据
            Blog blogDoc = blogHit.getContent();
            // 2.4 博客数据插入列表
            blogDocList.add(blogDoc);
        }
        return blogDocList;
    }


    @ApiOperation("列表")
    @GetMapping("listByTitleAndContentDsl")
    public List<Blog> listByTitleAndContentDsl(String title, String content) {
        return blogRepository.listByTitleAndContentDsl(title, content);
    }

    @ApiOperation("分页")
    @GetMapping("pageByTitleAndContentDsl")
    public Page<Blog> pageByTitleAndContentDsl(String title, String content) {
        PageRequest pageRequest = PageRequest.of(0, 2);
        return blogRepository.pageByTitleAndContentDsl(title, content, pageRequest);
    }


    @ApiOperation("通过标题和内容查询")
    @GetMapping("findByTitleAndContent")
    public void findByTitleAndContent(String title, String content) {

        List<SearchHit<Blog>> byTitleList = blogRepository.findByTitle(title);
        System.out.println(byTitleList);
        for (SearchHit<Blog> hit : byTitleList) {
            System.out.println(hit);
        }

        System.out.println("byTitleList:             " + byTitleList);
        List<Blog> byTitleAndContentList = blogRepository
                .findByTitleAndContent(title, content);
        System.out.println("byTitleAndContentList:   " + byTitleAndContentList);
        PageRequest page = PageRequest.of(0, 2);
        Page<Blog> byTitleAndContentPage = blogRepository.findByTitleAndContent(title, content, page);
        System.out.println("byTitleAndContentPage:   " + byTitleAndContentPage.getContent());
    }

    @ApiOperation("添加单个文档")
    @PostMapping("addDocument")
    public Blog addDocument() {
        Long id = 1L;
        Blog blog = new Blog();
        blog.setBlogId(id);
        blog.setTitle("Spring Data ElasticSearch学习教程" + id);
        blog.setContent("这是添加单个文档的实例" + id);
        blog.setAuthor("Tony");
        blog.setCategory("ElasticSearch");
        blog.setCreateTime(new Date());
        blog.setStatus(1);
        blog.setSerialNum(id.toString());

        return blogRepository.save(blog);
    }

    @ApiOperation("添加多个文档")
    @PostMapping("addDocuments")
    public Object addDocuments(Integer count) {
        List<Blog> blogs = new ArrayList<>();
        for (int i = 1; i <= count; i++) {
            Long id = (long) i;
            Blog blog = new Blog();
            blog.setBlogId(id);
            blog.setTitle("Spring Data ElasticSearch学习教程" + id);
            blog.setContent("这是添加单个文档的实例" + id);
            blog.setAuthor("Tony");
            blog.setCategory("ElasticSearch");
            blog.setCreateTime(new Date());
            blog.setStatus(1);
            blog.setSerialNum(id.toString());
            blogs.add(blog);
        }

        return blogRepository.saveAll(blogs);
    }

    /**
     * 跟新增是同一个方法。若id已存在，则修改。
     * 无法只修改某个字段，只能覆盖所有字段。若某个字段没有值，则会写入null。
     *
     * @return 成功写入的数据
     */
    @ApiOperation("修改单个文档")
    @PostMapping("editDocument")
    public Blog editDocument() {
        Long id = 1L;
        Blog blog = new Blog();
        blog.setBlogId(id);
        blog.setTitle("Spring Data ElasticSearch学习教程" + id);
        blog.setContent("这是修改单个文档的实例" + id);
        // blog.setAuthor("Tony");
        // blog.setCategory("ElasticSearch");
        // blog.setCreateTime(new Date());
        // blog.setStatus(1);
        // blog.setSerialNum(id.toString());

        return blogRepository.save(blog);
    }

    @ApiOperation("查找单个文档")
    @GetMapping("findById")
    public Blog findById(Long id) {
        return blogRepository.findById(id).get();
    }

    @ApiOperation("删除单个文档")
    @PostMapping("deleteDocument")
    public String deleteDocument(Long id) {
        blogRepository.deleteById(id);
        return "success";
    }

    @ApiOperation("删除所有文档")
    @PostMapping("deleteDocumentAll")
    public String deleteDocumentAll() {
        blogRepository.deleteAll();
        return "success";
    }


}
